home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Scene World 20
/
Scene_World_20_2013-01-04_People_of_Liberty_Scene_World_Magazine_Side_A.d64
/
! planetcode !
< prev
next >
Wrap
Text File
|
2023-02-26
|
17KB
|
705 lines
;--------------------------------------
;Scene World - Issue #19
;Assembly Games Programming Tutorial #2
;
;Planet Popper - Part 1 - The basics
;By Richard Bayliss.
;
;--------------------------------------
;This tutorial will get you started on
;how to create a simple shoot 'em up
;game. PLANET POPPER will show you
;examples of how to get sprites moving
;inside a loop. How the player can shoot
;and destroy the planets. As this is
;only the first example for the Planet
;Popper tutorial game. We will be taking
;a look at enhancing the game later on.
;Those who are using ACME, uncomment
;the following command. As this will
;be your filename for your program.
;If you're using Turbo Assembler on a
;Commodore 64, leave this commented.
;!to "planetp1.prg",cbm
;Okay, now to insert the music
;sprites files in Turbo Assembler,
;press the '_' and press SHIFT+L and
;enter the filename PLANETSPRITES. ACME
;cross assembler users, uncomment this
;to insert the sprites:
;* = $2000
;!bin "planetsprites.prg",,2
;Right now, we want some variables to
;represent various features that will
;be used in our game source.
playerx = $3000 ;Player ship X position
playery = $3001 ;Player ship Y position
bulletx = $3002;Player laser X position
bullety = $3003;Player laser Y position
planetpos = $3004 ;Planet positions
allsprites = $3000 ;Mainly for default
;positioning of all
;sprites.
collision = $3010;Collision registry for
;the sprites
bulletfired = $301e ;This will indicate
;whether the bullet
;has been fired or not
playerisdead = $301f;Guess what that is?
;Now our variables have been set, let's
;start the programming of our game. I
;think $4000 (sys13684) should do quite
;nicely, as this will only be a short
;program. :)
*= $4000 ;Remember this
restart
sei ;SEt Irq flag so no
;interrupts are in
;action.
lda #$00 ;Black screen and
;border.
sta $d020
sta $d021
lda #$14 ;Default C64 charset
sta $d018 ;(For now)
lda #$08 ;Screen multicolour
sta $d016 ;is off
lda #$ff ;All sprites enabled
sta $d015 ;and Multicolour is
sta $d01c ;Enabled as well
lda #$00 ;We don't want
sta $d017 ;Expanded sprites
sta $d01d ;...
sta $d01b ;or them behind the
;background.
lda #$0b ;Sprite multicolour1
sta $d025
lda #$0d ;Sprite multicolour2
sta $d026
;Clear the screen by filling it with the
;spacebar characters.
fillspace ldx #$00
filloop lda #$20
sta $0400,x
sta $0500,x
sta $0600,x
sta $06e8,x
inx
bne filloop
;Initialise the variables that represent
;that the player is alive, bullet isn't
;fired and all enemies are alive.
lda #$00
sta bulletfired
sta playerisdead
lda #$01
sta planet1d+1
sta planet2d+1
sta planet3d+1
sta planet4d+1
sta planet5d+1
sta planet6d+1
;Reposition all of the game sprites
;according to the table. We put this
;inside a loop and also inside the
;all sprites variable zero page ($02)
;as this will help us put all sprites
;in place. it will read the table using
;x and y positions. we use the hardware
;sprite positions later on in the main
;code.
ldx #$00
posloop lda startpos,x
sta allsprites,x
inx
cpx #$10
bne posloop
;Let's initialize the sprite frames and
;colours to how they should be.
ldx #$00
makespr lda frames,x
sta $07f8,x;Hardware spritetype
;in default VIC Bank
lda colours,x
sta $d027,x
inx
cpx #$08
bne makespr
;Main initialised bits are done, now
;we initialise an interrupt to get all
;sprites moving, and things going the
;way we want them to go.
lda #$7f
sta $dc0d ;CIA Interrupts on
lda #<irq ;Low bit of IRQ flag
ldx #>irq ;Hi bit of IRQ flag
sta $0314 ;Store to interrupt
stx $0315 ;Yep, and here too.
lda #$2e ;Start of top raster
sta $d012 ;(Move this if you
;need to).
lda #$1b
sta $d011 ;Switch screen on
lda #$01 ;Interrupt set
sta $d01a
cli ;Clear the IRQ flag
;It is time to synchronise the game
;code to how it should work. So let's
;get it working.
gameloop lda #$00
sta gamesync
cmp gamesync
beq *-3
jsr expmsb ;Expand MSB to full
jsr mvplan ;Move planets
jsr mvplr;Move player
jsr mvbull ;Move player bullet
jsr rdcoll ;Collision checks
jsr plan2bul;Planet to bullet
jsr plan2plr;Planet to player
jsr planoff ;Check if all off
jmp gameloop
;Expand the sprite MSB to full screen.
;We do this by placing the labels that
;indicate the sprite positions, into
;the actual hardware sprite positions.
;The use of $D010 expands the screen
;position for the sprites.
expmsb ldx #$00
exploop lda allsprites+1,x ;Y position
sta $d001,x ;Store to hardware
lda allsprites,x ;X position
asl a ;ACME, use just ASL
ror $d010 ;Rotate MSB to right
sta $d000,x ;Store to hardware
inx ;We increment twice
inx ;else we get silly
cpx #$10 ;results
bne exploop
rts ;End of subroutine
;Move the planets across the screen
;using a loop, that will allow them
;to move a certain speed.
mvplan ldx #$00
moveloop lda planetpos,x
clc
adc speedtable,x
sta planetpos,x
inx
cpx #$0c
bne moveloop
rts
;Now move the player using the joystick
;plugged into port 2. The player can
;only move left/right and shoot
mvplr lda $dc00;read joystick port 2
lsr a;up ;Read up - ignore
lsr a;down ;Read down - ignore
lsr a;left ;Read left - check
bcs joyright
lda playerx ;Read player x pos
sec ;Subtract by the
sbc #$02 ;speed of the movement
cmp #$0c ;Unless at #$0c
bcs storepx ;Store player X-Pos
lda #$0c ;Player at very left
storepx sta playerx
;We want to allow the player to be able
;to shoot a bullet, even if it is moving
;otherwise it won't look right. :)
jmp pushfire
joyright lsr a ;Read right - check
bcs pushfire
lda playerx ;Read player X-Pos
clc ;Add by the
adc #$02 ;Speed of the movement
cmp #$a2 ;Unless at #$a2 where
bcc storepx2 ;the player stops
lda #$a2 ;Player at very right
storepx2 sta playerx
;Read the fire button
pushfire lda $dc00
lsr a ;We call LSR 5 times, as
lsr a ;we check each direction
lsr a ;the fire button is
lsr a ;always the 5th check if
lsr a ;you try to read a
;joystick
bcs ignore ;No joy read if no
;fire button
;Unlike the movements of the player we
;make a check to see whether a bullet
;has already been fired. If it has then
;the bullet is active and we ignore
;positioning the bullet - otherwise it
;will look very odd indeed. ;)
checkbul lda bulletfired ;Read it ...
cmp #$01 ;If active ...
beq ignore ;Ignore bullet
;Since the bullet is not active or
;moving here, we shall reposition the
;bullet at exactly the same location as
;where the player lies. This makes fair
;shooting.
lda playerx ;Read player X pos
sta bulletx ;Store bullet X pos
lda playery ;Read player Y pos
sta bullety ;Store bullet Y pos
;Now because we only want the bullet to
;appear once until offset, we activate
;the logic switch to bulletfired.
lda #$01
sta bulletfired
ignore rts ;Terminate routine
;This is our main routine which will
;fire the bullet upscreen, until it has
;reached the top border of the screen.
;We only want the bullet to go upwards
;being that this shooting game's meant
;to be like that :)
mvbull lda bulletfired ;Check if the
cmp #$01 ;bullet is
beq mvbull2 ;active, else
rts ;ignore it.
mvbull2 lda bullety ;Read Y pos of the
sec ;player's bullet
sbc #$08 ;subtract by 6 for
;speed.
cmp #$20 ;Has the bullet
;left the top
;border?
bcs bullset ;bullet still on
;Assuming that the bullet reaches the
;top and left the raster position #$20
;we can stop the bullet from moving,
;simply by changing the value of #$00
;to bullet fired. So that the player is
;ready to shoot another bullet.
lda #$00 ;Bullet inactive
sta bulletfired
rts
;Else keep the bullet moving
bullset sta bullety
rts
;Now for something, a shooting game
;can't be without. Yes, that's right
;a collision register. To do the
;collision. We are using a software box
;sprite/sprite check. There are two
;types of collision you'll want. Either
;a player/enemy or a bullet/enemy type
;collision.
;Start with storing values to
;each top/bottom, left/right
;position inside the boxed area
;It looks something like this
; ^
; sec:
;s sbc:
;b :
;c sec : clc
; _--sbc------0------adc----
; :
;a clc:
;d adc:
;c :
; :
rdcoll lda playerx ;Read player's ypos
sec
sbc #$06 ;Subtract Y pos by 6
sta collision ;store check 1
clc
adc #$0c ;Add Y pos by 12
sta collision+1 ;store check 2
lda playery ;Read player's xpos
sec
sbc #$0c ;Subtract by 12
sta collision+2
clc
adc #$18 ;Add by 32
sta collision+3
;Now read the bullet and store to
;the collision registry, as we have done
;before to the player's registry.
lda bulletx
sec
sbc #$06
sta collision+4 ;Exactly the
clc ;same method
adc #$0c
sta collision+5
lda bullety
sec
sbc #$0c
sta collision+6
clc
adc #$18
sta collision+7
rts
;Check whether or not the enemy hits
;the player's bullet (or vice versa) as
;we want to show that a collision has
;been made. we do this check for each
;planet. using a loop is usually easier
;blended into the game.
plan2bul
ldx #$00 ;Read inside
bcolloop lda planetpos,x ;the co-ords
cmp collision+4 ;of the
bcc planetsok ;collision
cmp collision+5 ;area, other-
bcs planetsok ;wise there is
lda planetpos+1,x ;no collision
cmp collision+6 ;(jump to
bcc planetsok ;planetsok),
cmp collision+7 ;else planets
bcs planetsok ;are hit.
;The collision test has passed. The
;bullet has entered the collision area
;of the planet. So we now stop a chosen
;planet. Reset the bullet and stop the
;loop as well. Oh, and reactivate the
;ability for the player to shoot again.
lda #$00 ;Of which
sta planetpos,x ;planets
sta planetpos+1,x
sta bulletx
sta bullety
sta planet1d,x ;The chosen
;direction
sta planet1d+1,x
sta bulletfired
rts
planetsok inx
inx
cpx #$0c
bne bcolloop
rts
;Now check whether or not the player's
;ship has been hit by the planets. If
;the player has been hit, then remove
;all sprites and display 'you're dead'
;on to the screen.
plan2plr ldx #$00
colloop2 lda planetpos,x
cmp collision,x
bcc nokill
cmp collision+1,x
bcs nokill
lda planetpos+1,x
cmp collision+2,x
bcc nokill
cmp collision+3,x
bcs nokill
jmp iamdead
;Collision has failed, as the planets
;are on the outside area, so we move on
;to the next planet inside the loop.
nokill inx
inx
cpx #$0c ;Both X+Y positions
;per planet
bne colloop2 ;return to loop
rts
;The player is dead, so now we just
;show the game over message - or similar
;and wait for the player to press SPACE
;BAR to try again.
iamdead lda #$00
sta $d015 ;Clear all sprites
ldx #$00
deadloop lda deadtext,x ;Check if text
cmp #$40 ;is higher than
bcc dontswap ;character #$40
sec ;then change to
sbc #$40 ;correct case.
dontswap
sta $059c,x ;Screen char pos.
lda #$0a ;Light red text
sta $d99c,x;Screen char colour
inx
cpx #$0e
bne deadloop
;Wait for the player to respond by
;pressing the spacebar. After the space
;bar has been pressed. We shall restart
;the game again.
waitspace lda $dc01 ;Read port 1 / keys
lsr a
lsr a
lsr a
lsr a
lsr a ;5x = Spacebar
bcs waitspace
jmp restart ;Restart game
;Check if/not all planets are off screen
;if they are still moving then keep them
;on. Otherwise reset the planet's pos.
planoff lda planet1d
cmp #$00
bne notoff
lda planet1d+1
cmp #$00
bne notoff
lda planet2d
cmp #$00
bne notoff
lda planet2d+1
cmp #$00
bne notoff
lda planet3d
cmp #$00
bne notoff
lda planet3d+1
cmp #$00
bne notoff
lda planet4d
cmp #$00
bne notoff
lda planet4d+1
cmp #$00
bne notoff
lda planet5d
cmp #$00
bne notoff
lda planet5d+1
cmp #$00
bne notoff
lda planet6d
cmp #$00
bne notoff
lda planet6d+1
cmp #$00
bne notoff
;Reset the planet sprite's positions
;as we have done before and move them
;again.
ldx #0
resetpos lda startpos+4,x
sta planetpos,x
inx
cpx #$0c
bne resetpos
ldx #$00
resetspd lda #$01
sta planet1d+1,x
inx
inx
cpx #$0c
bne resetspd
notoff
rts
;This is our main IRQ interrupt to
;allow a more synchronised game to
;operate.
irq asl $d019 ;ACK interrupt
lda $dc0d
sta $dd0d ;VIC CIA again
lda #$fa ;Bottom raster pos
sta $d012 ;
inc gamesync ;Add 1 to the game
;sync mode
pla ;End of interruot
tay
pla
tax
pla
rti
;Sprite starting position X/Y tables.
;ACME users, change .byte to !byte as
;the pseudo's completely different to
;both assemblers. :)
startpos .byte $58,$e0 ;Player X/Y
.byte $00,$00 ;Bullet (Offset)
.byte $1c,$40 ;Planet 1 Left
.byte $30,$80 ;Planet 2 Left
.byte $4c,$c0 ;Planet 3 Left
.byte $70,$50 ;Planet 4 Right
.byte $8c,$10 ;Planet 5 Right
.byte $a0,$20 ;Planet 6 Right
;Sprite colour table. Well, the sprites
;are in fact planets. So for now, let's
;make the planet sprites blue as main
;colour. Player ship can be white.
colours .byte $01,$01,$06,$06
.byte $06,$06,$06,$06
;Sprite object frames (Player, bullet
;and planets as those are all we have
;for this little example. No animation
;whatsoever. This will be for part 2
;where we jazz things up a bit.
frames .byte $80,$81,$82,$82
.byte $82,$82,$82,$82
;Enemy speed table. As we are going to
;move the sprite dwon the screen
;we will be using the speed table.
;The table will be marked
;as 0 if a planet has been popped.
speedtable
planet1d .byte $00,$01;Planet 1 X / Y
planet2d .byte $00,$01;Planet 2 X / Y
planet3d .byte $00,$01;Planet 3 X / Y
planet4d .byte $00,$01;Planet 4 X / Y
planet5d .byte $00,$01;Planet 5 X / Y
planet6d .byte $00,$01;Planet 6 X / Y
;Synchronizer switch, so outside of the
;IRQ interrupt we get the code in sync.
gamesync .byte $00
;The dead message ACME users, change
;this to !text instead. ;)
deadtext .text "you are dead!"
;And there we have it, a simple game
;tutorial. It may take a while for you
;to get used to this code. But I'm sure
;that you'll get somewhere with it.
;
;NEXT ISSUE:
;
;We will be expanding this game a bit
;further, by adding some more control
;to the planets and also jazz things up
;more with a nice game background,
;animated sprites. Also we'll be adding
;a scoring system and sound to it.
;Stay tuned.